package state


import ."api"
import "number"
import "fmt"


type luaValue interface{}	// 使用空接口来表示不同类型的Lua值


func typeOf(val luaValue) LuaType {

	switch val.(type) {

		case nil:	return LUA_TNIL

		case bool:	return LUA_TBOOLEAN

		case int64:	return LUA_TNUMBER

		case float64:	return LUA_TNUMBER

		case string:	return LUA_TSTRING

		case *luaTable:	return LUA_TTABLE

		case *closure:	return LUA_TFUNCTION

		case *luaState:	return LUA_TTHREAD

		default:	panic("TODO!")

	}

}


// 任意值转为浮点数
func convertToFloat(val luaValue) (float64, bool) {

	switch x := val.(type) {

		case float64:		return x, true

		case int64:			return float64(x), true

		case string:		return number.ParseFloat(x)

		default:			return 0, false
	}
}


// 任意值转为整数
func convertToInteger(val luaValue) (int64, bool) {

	switch x := val.(type) {

		case int64:			return x, true

		case float64:		return number.FloatToInteger(x)

		case string:		return _stringToInteger(x)

		default:			return 0, false
	}
}


// 字符串转为整数
func _stringToInteger(s string) (int64, bool) {

	if i, ok := number.ParseInteger(s); ok {

		return i, true
	}

	if f, ok := number.ParseFloat(s); ok {

		return number.FloatToInteger(f)
	}

	return 0, false
}


// 给值关联元表
// 先判断是否是表，如果是，直接修改其元表字段即可
// 否则的话，根据变量类型把元表存储在注册表里
// 这样就达到了按类型共享元表的目的
// 虽然注册表也是一个普通的表，不过按照约定
// 下划线开头后跟大写字母的字段名是保留给Lua实现使用的
// 所以使用了"_MT1"这样的字段名，以免和用户(通过API)放在注册表里的数据产生冲突
// 如果传递给函数的是nil值，效果就相当于删除元表
func setMetatable(val luaValue, mt *luaTable, ls *luaState) {

	if t, ok := val.(*luaTable); ok {

		t.metatable = mt

		return

	}

	key := fmt.Sprintf("_MT%d", typeOf(val))

	ls.registry.put(key, mt)

}


// 返回与给定值关联的元表
// 先判断值是否是表
// 如果是，直接返回其元表字段即可
// 否则的话，根据值的类型从注册表里取出与该类型关联的元表并返回
// 如果值没有元表与之关联，返回值就是nil
func getMetatable(val luaValue, ls *luaState) *luaTable {

	if t, ok := val.(*luaTable); ok {

		return t.metatable

	}

	key := fmt.Sprintf("_MT%d", typeOf(val))

	if mt := ls.registry.get(key); mt != nil {

		return mt.(*luaTable)

	}

	return nil

}


// 负责查找并调用元方法
// 接收4個參數, 返回兩個值
// 前两个参是算术运算的两个操作数
// 第3个参数给出元方法名
// 如果操作数不是表，则需要通过第4个参数来访问注册表，从中查找元表
// 第一个返回值是元方法执行的结果
// 但由于元方法可能会返回任何值，包括nil和false，
// 所以我们只能使用第二个返回值来表示能否找到元方法
func callMetamethod(a, b luaValue, mmName string, ls *luaState) (luaValue, bool) {

	var mm luaValue

	if mm = getMetafield(a, mmName, ls); mm == nil {

		if mm = getMetafield(b, mmName, ls); mm == nil {

			return nil, false

		}

	} 

	ls.stack.check(4)
	ls.stack.push(mm)
	ls.stack.push(a)
	ls.stack.push(b)

	ls.Call(2, 1)

	return ls.stack.pop(), true
}


// 获得元表中指定字段的值
func getMetafield(val luaValue, fieldName string, ls *luaState) luaValue {

	if mt := getMetatable(val, ls); mt != nil {

		return mt.get(fieldName)

	}

	return nil

}